/*
* This file is a part of the open-eBackup project.
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
* If a copy of the MPL was not distributed with this file, You can obtain one at
* http://mozilla.org/MPL/2.0/.
*
* Copyright (c) [2024] Huawei Technologies Co.,Ltd.
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*/
#include "NasReplicationService.h"
#include <ctime>
#include "common/CleanMemPwd.h"
#include "nas_share/common/HetroCommonService.h"
#include "KmcEncrypt.h"
#include "securec.h"
#include "system/System.hpp"

using namespace nasreplicationservice;
using namespace PluginUtils;
using namespace Module;


static std::mutex m_nasReplicationServiceMutex;
namespace {
constexpr int NUM_2 = 2;
constexpr int NUM_3 = 3;
constexpr int NUM_5 = 5;
constexpr int NUM_10 = 10;
constexpr int INTERVAL_TIME_IN_SECOND = 1;
constexpr int16_t NOT_SYNCHRONIZING = -1;
constexpr uint16_t PM_COPIES_CATALOG_PORT = 30093;
constexpr uint16_t SCAN_TIME_INTERVAL_SECEND = 5;
constexpr uint16_t TIME_INTERVAL_20_SECEND = 20;
constexpr uint16_t TIME_OUT_3_MIN = 3 * 60;
constexpr uint16_t TIME_INTERVAL_120_SECEND = 120;
constexpr uint16_t RANDOM_MAX_LEN = 2;
constexpr uint16_t TWO_KB = 2048;
const uint32_t  RECON_TIMER_INTERVAL = 1 * 60 * 1000;
constexpr auto MODULE = "NasReplicationService";
constexpr auto SNAP_TAG = "dpa_snapshot";
constexpr uint32_t REMOTE_DEVICE_STATUS_FAULT = 2;
}

long int GetRandBytes(unsigned char bytes[], unsigned int len)
{
    memset_s(bytes, len, 0, len);

#ifdef __WINDOWS__
    HCRYPTPROV hCryptProv = NULL;
    if (!CryptGenRandom(hCryptProv, len, (BYTE*)bytes)) {
        return -1;
    }
    long int ret = len;
#else
    int fd = open("/dev/random", O_RDONLY);
    if (fd < 0) {
        return -1;
    }
    long int ret = read(fd, bytes, len);
    if (ret <= 0) {
        close(fd);
        return -1;
    }

    close(fd);
#endif

    return ret;
}

int GetRandBytesFixLength(unsigned char bytes[], unsigned int len)
{
    int ret = 0;
    unsigned char *pBytes = bytes;
    while (ret < static_cast<int>(len)) {
        long int result = GetRandBytes(pBytes + ret, len - ret);
        if (result < 0) {
            return -1;
        }
        ret += result;
    }
    return len;
}

namespace NasRepServiceObjOp {
NasReplicationService::NasReplicationService(const std::string& productFSID, const std::string& basicFSID,
    const uint64_t& qos)
    : m_qos(qos),
    m_productFSID(productFSID),
    m_basicFSID(basicFSID)
{
    return;
}

NasReplicationService::~NasReplicationService()
{
    HCP_Log(INFO, MODULE) << "Enter destructor of NasReplicationService" << HCPENDLOG;
    Module::CleanMemoryPwd(m_remoteDeviceUserPassword);
    return;
}

void NasReplicationService::SetTaskID(const std::string& mainTaskId)
{
    HCP_Log(INFO, MODULE) << "Set main task id " << mainTaskId << HCPENDLOG;
    m_mainTaskId = mainTaskId;
}

void NasReplicationService::SetProduckIscsiList(const std::vector<std::string>& repPortNameList)
{
    m_productIscsiList = repPortNameList;
}

std::unique_ptr<NasReplicationService> NasReplicationService::GetReplicationInstance(
    const std::string& productFSID, const std::string& basicFSID, const uint64_t& qos)
{
    std::lock_guard<std::mutex> lock(m_nasReplicationServiceMutex);
    std::unique_ptr<NasReplicationService> nasReplicationServiceObj =
        std::make_unique<NasReplicationService>(productFSID, basicFSID, qos);
    return nasReplicationServiceObj;
}

bool NasReplicationService::CheckReplicatonPairInBaic(std::shared_ptr<DoradoNas>& basicController,
    ReplicationPairInfo replicationPairInfo)
{
    if (replicationPairInfo.status == INVALIID) {
        HCP_Log(ERR, MODULE) << "Pair is invalid, pairID: " << replicationPairInfo.pairID << HCPENDLOG;
        return false;
    }
    HCP_Log(INFO, MODULE) << "Remote fs is " << WIPE_SENSITIVE(replicationPairInfo.remoteResName) << HCPENDLOG;
    DeviceDetails info;
    if (basicController->Query(info) != MP_SUCCESS) {
        HCP_Log(ERR, MODULE) << "Not found local fs "
            << WIPE_SENSITIVE(replicationPairInfo.remoteResName) << HCPENDLOG;
        return false;
    }
    if (replicationPairInfo.remoteResName != info.deviceName) {
        HCP_Log(ERR, MODULE) << "Pair is invalid, basic pair name" << WIPE_SENSITIVE(info.deviceName)
            << "remote pair name" << WIPE_SENSITIVE(replicationPairInfo.remoteResName) << HCPENDLOG;
        return false;
    }
    return true;
}

NasRepServiceObjOp::NasReplicationServiceErrorCode NasReplicationService::PrepareRemoteReplicatonPair
(std::shared_ptr<DoradoNas>& basicController,
    std::shared_ptr<DoradoNas>& productController)
{
    auto ReplicationServiceNormal = NasRepServiceObjOp::NasReplicationServiceErrorCode::ReplicationServiceNormal;
    auto ReplicationServiceAbNormal = NasRepServiceObjOp::NasReplicationServiceErrorCode::ReplicationServiceAbNormal;
    auto ReplicationDeviceFault = NasRepServiceObjOp::NasReplicationServiceErrorCode::ReplicationDeviceFault;
    NasRepServiceObjOp::NasReplicationServiceErrorCode remoteDeviceAndBindRepPortGroupsRet =
    CreateRemoteDeviceAndBindRepPortGroups(basicController, productController);
    if (remoteDeviceAndBindRepPortGroupsRet != ReplicationServiceNormal) {
        return remoteDeviceAndBindRepPortGroupsRet;
    }
    std::string pairID;
    ReplicationPairInfo replicationPairInfo;
    // pair已经存在的情况下,看pair状态是否OK, 业务代码中调用IsExistPairInvalid判断pair是否有效
    // 备份场景，通过生产资源信息查找pair，pair存在且有效，则直接使用，如果pair不存在则创建新pair
    // 恢复场景，通过生产资源信息查找pair， pair存在则需要检查是否与恢复源一致，一致则直接使用，否则不能再
    if (GetPairInfoInProduct(productController, basicController, pairID)) {
        if (!GetPairInfoByID(productController, pairID, replicationPairInfo)) {
            HCP_Log(ERR, MODULE) << "Get pairInfo by ID fail, pairID" << pairID << HCPENDLOG;
            SetExistPairValid(false);
            return ReplicationServiceNormal;
        }
        if (m_basicFSID != replicationPairInfo.remoteResID) {
            HCP_Log(ERR, MODULE) << "m_basicFSID " << m_basicFSID << " is different from remoteResID "
                << replicationPairInfo.remoteResID << HCPENDLOG;
            SetExistPairValid(false);
            return ReplicationServiceAbNormal;
        }
        if (!CheckReplicatonPairInBaic(basicController, replicationPairInfo)) {
            SetExistPairValid(false);
            return ReplicationServiceAbNormal;
        } else {
            SetExistPairValid(true);
        }
        m_pairID = pairID;
        if (!IsExistPairValid()) {
            return ReplicationServiceNormal;
        }
        if (!UpdateReplicationPairBandWidth(productController, pairID)) {
            HCP_Log(ERR, MODULE) << "UpdateReplicationPairBandWidth fail, " << pairID << HCPENDLOG;
            return ReplicationServiceAbNormal;
        }
    }
    return ReplicationServiceNormal;
}

void NasReplicationService::SetExistPairValid(bool validFlag)
{
    m_isExistPairValid = validFlag;
    return;
}

bool NasReplicationService::IsExistPairValid()
{
    return m_isExistPairValid;
}

bool NasReplicationService::UpdateReplicationPairBandWidth(std::shared_ptr<DoradoNas>& deviceController,
    const std::string& pairID)
{
    if (pairID == "") {
        HCP_Log(INFO, MODULE) << "PairId is NULL " << HCPENDLOG;
        return true;
    }
    int ret = deviceController->UpdateReplication(m_qos, pairID);
    if (ret != MP_SUCCESS) {
        HCP_Log(ERR, MODULE)
            << "Update ReplicationPair failed. PairID: "<< pairID<< HCPENDLOG;
        return false;
    }
    HCP_Log(INFO, MODULE) << "Update ReplicationPair success. Qos:" << m_qos << HCPENDLOG;
    return true;
}

bool NasReplicationService::GetRemoteDeviceUserPwd(const std::string &deviceUser)
{
    AppProtect::ResourceStatus resourceStatus;
    bool existResource = true;
    if (!HetroCommonService::QueryResource(m_remoteDeviceUserName, resourceStatus, m_mainTaskId)) {
        m_remoteDeviceUserPassword = GeneratePassword();
        HCP_Log(INFO, MODULE) << "Query nothing from remote device resource key." << HCPENDLOG;
        return false;
    }
    // createResource时，字符串前后会加上双引号，此处去除字符串前后的双引号;
    std::string cipherPlaintxt = resourceStatus.resource.resourceValue;
    cipherPlaintxt.erase(remove(cipherPlaintxt.begin(), cipherPlaintxt.end(), '\"'), cipherPlaintxt.end());
    if (cipherPlaintxt == "") {
        HetroCommonService::DeleteResource(m_remoteDeviceUserName, m_mainTaskId);
        HCP_Log(INFO, MODULE) << "Got null resource value for remote device user: " << m_remoteDeviceUserName
            << "." << HCPENDLOG;
        m_remoteDeviceUserPassword = GeneratePassword();
        return false;
    }

    if (KmcEncrypt::Decrypt(m_remoteDeviceUserPassword, cipherPlaintxt) == MP_FAILED) {
        HCP_Log(WARN, MODULE) << "Failed to encrypt remote device password, generate the password again" << HCPENDLOG;
        HetroCommonService::DeleteResource(m_remoteDeviceUserName, m_mainTaskId);
        m_remoteDeviceUserPassword = GeneratePassword();
        Module::CleanMemoryPwd(cipherPlaintxt);
        return false;
    }
    Module::CleanMemoryPwd(cipherPlaintxt);
    return true;
}

bool NasReplicationService::CreateRemoteDeviceAdministrator(std::shared_ptr<DoradoNas> &basicController)
{
    // 首选从数据库中获取远端设备管理员，如果无信息，则要创建
    m_remoteDeviceUserName = "NAS_Protect";
    bool existResource = true;
    if (!GetRemoteDeviceUserPwd(m_remoteDeviceUserName)) {
        existResource = false;
    }
    std::string remoteDevUserID = "";
    if (basicController->QueryRemoteDeviceUserByName(m_remoteDeviceUserName, remoteDevUserID) == MP_SUCCESS) {
        if (basicController->DeleteRemoteDeviceUserByID(remoteDevUserID) != MP_SUCCESS) {
            HCP_Log(ERR, MODULE) << "The existed RemoteDeviceUser " << WIPE_SENSITIVE(m_remoteDeviceUserName)
                << " cannot be deleted!" << HCPENDLOG;
            return false;
        }
    }
    if (basicController->CreateRemoteDeviceUser(m_remoteDeviceUserName, m_remoteDeviceUserPassword) !=
        MP_SUCCESS) {
        HCP_Log(ERR, MODULE) << "Create remoteDeviceUser " << WIPE_SENSITIVE(m_remoteDeviceUserName)
            << " fail" << HCPENDLOG;
        return false;
    }
    if (!existResource) {
        std::string encryptPlaintext {};
        if (KmcEncrypt::Encrypt(m_remoteDeviceUserPassword, encryptPlaintext) == MP_SUCCESS) {
            HetroCommonService::CreateResource(m_remoteDeviceUserName, encryptPlaintext, m_mainTaskId);
            HCP_Log(INFO, MODULE) << "Create remote device user resource successfully" << HCPENDLOG;
        }
    }
    return true;
}

bool NasReplicationService::GetLogicPortNameList(std::shared_ptr<DoradoNas>& productController,
    std::shared_ptr<DoradoNas>& basicController)
{
    // 如果没有指定生产端的复制逻辑端口，就去生产端查全量的复制逻辑端口
    int ret;
    if (m_productIscsiList.empty()) {
        ret = productController->GetLogicPortNameList(m_productIscsiList);
        if (ret != MP_SUCCESS) {
            HCP_Log(ERR, MODULE) << "Get local logic port name list failed!" << HCPENDLOG;
            return false;
        }
    }
    for (const std::string& name : m_productIscsiList) {
        INFOLOG("use product rep port: %s", name.c_str());
    }
    std::vector<LogicalPorts> basicReplicationPortList = {};
    ret = basicController->QueryReplicationPortIpInfo(basicReplicationPortList);
    if (ret != MP_SUCCESS) {
        HCP_Log(ERR, MODULE) << "Get local logic port name list failed!" << HCPENDLOG;
        return false;
    }
    for (auto serviceIps : basicReplicationPortList) {
        if (serviceIps.ipv6Address == "") {
            continue;
        }
        m_basicIscsiList.push_back(serviceIps.name);
        m_basicIscsiIpList.push_back(serviceIps.ipv6Address);
        HCP_Log(DEBUG, MODULE) << "Get servieIp: "<< (serviceIps.ipv6Address) << HCPENDLOG;
    }
    for (auto serviceIps : basicReplicationPortList) {
        if (serviceIps.ipv4Address == "") {
            continue;
        }
        m_basicIscsiList.push_back(serviceIps.name);
        m_basicIscsiIpList.push_back(serviceIps.ipv4Address);
        HCP_Log(DEBUG, MODULE) << "Get servieIp: "<< (serviceIps.ipv4Address) << HCPENDLOG;
    }
    return true;
}

bool NasReplicationService::CreateReplicationRepportgroup(std::shared_ptr<DoradoNas>& basicController,
    std::shared_ptr<DoradoNas>& productController, std::string& productGroupID, std::string& basicGroupID)
{
    std::string productEsn;
    std::string basicEsn;
    if (productController->GetESN(productEsn) != MP_SUCCESS || basicController->GetESN(basicEsn) != MP_SUCCESS) {
        return false;
    }
    std::string productGroupName = "Reppg" + productEsn;
    std::string basicGroupName = "Reppg" + basicEsn;
    int ret = productController->CreateReplicationRepportgroup(m_productIscsiList,
        productGroupName, productGroupID);
    ret |= basicController->CreateReplicationRepportgroup(
        m_basicIscsiList, basicGroupName, basicGroupID);
    if (ret != MP_SUCCESS) {
        HCP_Log(ERR, MODULE) << "Create Remote Repportgroup failed!" << HCPENDLOG;
        return false;
    }

    HCP_Log(DEBUG, MODULE) << "Create Remote Repportgroup success! remoteGroupID: "
        << basicGroupID << HCPENDLOG;
    return true;
}

bool NasReplicationService::AddLocalAndRemotePort(std::shared_ptr<DoradoNas>& productController,
    std::shared_ptr<DoradoNas>& basicController, const std::string& basicDeviceID)
{
    std::string productGroupId;
    std::string basicGroupId;
    std::string basicESN;
    int ret =
        productController->QueryRemoteDevice(basicDeviceID, productGroupId, basicGroupId, basicESN);
    if (ret != MP_SUCCESS) {
        HCP_Log(ERR, MODULE) << "QueryRemoteDevice failed, start CreateRemoteDevice." << HCPENDLOG;
        return false;
    }

    if (productController->AddReplicationRepportgroupMember(productGroupId, m_productIscsiList) == MP_SUCCESS &&
        basicController->AddReplicationRepportgroupMember(basicGroupId, m_basicIscsiList) == MP_SUCCESS) {
        HCP_Log(INFO, MODULE) << "AddReplicationRepportgroupMember succeed." << HCPENDLOG;
        return true;
    }
    HCP_Log(ERR, MODULE) << "remoteGroupId AddReplicationRepportgroupMember error." << HCPENDLOG;
    return false;
}

bool NasReplicationService::CreateRemoteDevice(std::shared_ptr<DoradoNas>& productController)
{
    unsigned int i = 0;
    while (i < m_productIscsiList.size()) {
        unsigned int j = 0;
        while (j < m_basicIscsiIpList.size()) {
            std::string productIscsi = m_productIscsiList[i];
            std::string localIscsiIp = m_basicIscsiIpList[j];
            productController->SetRetryAttr(1);
            int ret = productController->CreateRemoteDevice(productIscsi, localIscsiIp,
                m_remoteDeviceUserName, m_remoteDeviceUserPassword, m_remoteDeviceID);
            if (ret != MP_SUCCESS) {
                HCP_Log(WARN, MODULE) << "Create RemoteDevice failed!" << HCPENDLOG;
                j++;
                continue;
            }
            break;
        }
        if (j < m_basicIscsiIpList.size()) {
            break;
        }
        i++;
    }
    productController->SetRetryAttr(NUM_3);
    Module::CleanMemoryPwd(m_remoteDeviceUserPassword);
    if (i == m_productIscsiList.size()) {
        HCP_Log(ERR, MODULE) << "Create RemoteDevice failed!" << HCPENDLOG;
        return false;
    }
    HCP_Log(INFO, MODULE) << "CreateRemoteDevice Succeed." << HCPENDLOG;
    return true;
}

bool NasReplicationService::RelinkRemoteDevice(std::shared_ptr<DoradoNas>& productController)
{
    unsigned int i = 0;
    while (i < m_productIscsiList.size()) {
        unsigned int j = 0;
        while (j < m_basicIscsiIpList.size()) {
            std::string productIscsi = m_productIscsiList[i];
            std::string localIscsiIp = m_basicIscsiIpList[j];
            productController->SetRetryAttr(1);
            int ret = productController->RelinkRemoteDevice(productIscsi, localIscsiIp,
                m_remoteDeviceUserName, m_remoteDeviceUserPassword, m_remoteDeviceID);
            if (ret != MP_SUCCESS) {
                HCP_Log(WARN, MODULE) << "Relink RemoteDevice failed!" << HCPENDLOG;
                j++;
                continue;
            }
            break;
        }
        if (j < m_basicIscsiIpList.size()) {
            break;
        }
        i++;
    }
    productController->SetRetryAttr(NUM_3);
    Module::CleanMemoryPwd(m_remoteDeviceUserPassword);
    if (i == m_productIscsiList.size()) {
        HCP_Log(ERR, MODULE) << "Relink RemoteDevice failed!" << HCPENDLOG;
        return false;
    }
    HCP_Log(INFO, MODULE) << "RelinkRemoteDevice Succeed." << HCPENDLOG;
    return true;
}

bool NasReplicationService::CreateRemoteDeviceTaskInner(std::shared_ptr<DoradoNas>& basicController,
    std::shared_ptr<DoradoNas>& productController)
{
    std::string productGroupID = "";
    std::string basicGroupID = "";
    if (!CreateRemoteDevice(productController)) {
        HCP_Log(ERR, MODULE) << "Create RemoteDevice failed." << HCPENDLOG;
        return false;
    }
    HCP_Log(INFO, MODULE) << "Create RemoteDevice success." << HCPENDLOG;
    if (!CreateReplicationRepportgroup(basicController, productController, productGroupID, basicGroupID)) {
        return false;
    }
    int ret =
        productController->BindRepportgroupsToRemoteDevice(m_remoteDeviceID, productGroupID, basicGroupID);
    if (ret != MP_SUCCESS) {
        HCP_Log(ERR, MODULE) << "Bind Repportgroups to RemoteDevice failed!" << HCPENDLOG;
        return false;
    }
    return true;
}

NasRepServiceObjOp::NasReplicationServiceErrorCode NasReplicationService::CreateRemoteDeviceAndBindRepPortGroups
(std::shared_ptr<DoradoNas>& basicController,
    std::shared_ptr<DoradoNas>& productController)
{
    // 在生产端添加底座Dorado为远端设备
    if (!GetLogicPortNameList(productController, basicController)) {
        HCP_Log(ERR, MODULE) << "Get LogicPortName list failed." << HCPENDLOG;
        return NasRepServiceObjOp::NasReplicationServiceErrorCode::ReplicationServiceAbNormal;
    }
    HCP_Log(DEBUG, MODULE) << "Get LogicPortName list success." << HCPENDLOG;
    std::string basicEsn = "";
    std::string basicDeviceID = "";
    // step1：首先根据ESN查看远端设备是否存在
    if (basicController->GetESN(basicEsn) != MP_SUCCESS) {
        HCP_Log(ERR, MODULE) << "Failed to get the esn of local storage." << HCPENDLOG;
        return NasRepServiceObjOp::NasReplicationServiceErrorCode::ReplicationServiceAbNormal;
    }
    ReplicationDeviceManager::GetInstance().Wait();
    HCP_Log(INFO, MODULE) << "Enter Get Remote Device, wait finish" << HCPENDLOG;
    // step2: 远端设备存在的情况下，尝试增加链路
    int healthStatus = 0;
    int runStatus = 0;
    if (productController->BatchQueryRemoteDevice(basicEsn, basicDeviceID, healthStatus, runStatus) == MP_SUCCESS) {
        HCP_Log(DEBUG, MODULE) << "RemoteDevice already exist. remoteDeviceID: "<< basicDeviceID << HCPENDLOG;
        m_remoteDeviceID = basicDeviceID;
        // step3: 远端设备不存在的情况下创建远端设备并绑定端口组
        if (!AddLocalAndRemotePort(productController, basicController, basicDeviceID)) {
            HCP_Log(ERR, MODULE) << "AddLocalAndRemotePort failed." << HCPENDLOG;
            ReplicationDeviceManager::GetInstance().Signal();
            HCP_Log(ERR, MODULE) << "Leave Get Remote Device, signal finish" << HCPENDLOG;
            return NasRepServiceObjOp::NasReplicationServiceErrorCode::ReplicationServiceAbNormal;
        }
        if (healthStatus == REMOTE_DEVICE_STATUS_FAULT) {
            ReplicationDeviceManager::GetInstance().Signal();
            return NasRepServiceObjOp::NasReplicationServiceErrorCode::ReplicationDeviceFault;
        }
        ReplicationDeviceManager::GetInstance().Signal();
        HCP_Log(INFO, MODULE) << "Leave Get Remote Device, signal finish" << HCPENDLOG;
        return NasRepServiceObjOp::NasReplicationServiceErrorCode::ReplicationServiceNormal;
    }
    HCP_Log(INFO, MODULE) << "Start First Create remote device " << HCPENDLOG;
    if (!FirstCreateRemoteDeviceInfo(basicController, productController, basicEsn)) {
        ReplicationDeviceManager::GetInstance().Signal();
        HCP_Log(ERR, MODULE) << "Create remote device failed." << HCPENDLOG;
        return NasRepServiceObjOp::NasReplicationServiceErrorCode::ReplicationServiceAbNormal;
    }
    ReplicationDeviceManager::GetInstance().Signal();
    HCP_Log(INFO, MODULE) << "Leave Create Remote Device, signal finish" << HCPENDLOG;
    return NasRepServiceObjOp::NasReplicationServiceErrorCode::ReplicationServiceNormal;
}

bool NasReplicationService::FirstCreateRemoteDeviceInfo(std::shared_ptr<DoradoNas>& basicController,
    std::shared_ptr<DoradoNas>& productController, std::string basicEsn)
{
    std::string productEsn;
    if (productController->GetESN(productEsn) != MP_SUCCESS) {
        HCP_Log(ERR, MODULE) << "Failed to get the esn of product storage." << HCPENDLOG;
        return false;
    }
    timeval curTime {};
    gettimeofday(&curTime, nullptr);
    std::string seconds = std::to_string(curTime.tv_sec);
    std::string productEsnKey = MODULE + productEsn;
    AppProtect::ResourceStatus resourceStatus;
    std::string basicDeviceID;
    int checkCount = 0;
    do {
        if (HetroCommonService::QueryResource(productEsnKey, resourceStatus, m_mainTaskId)) {
            if (curTime.tv_sec - std::stoi(resourceStatus.resource.resourceValue) > TIME_OUT_3_MIN) {
                HetroCommonService::DeleteResource(productEsnKey, m_mainTaskId);
                continue;
            }
            break;
        }
        HCP_Log(INFO, MODULE) << "Query nothing from remote device resource key." << HCPENDLOG;
        if (!HetroCommonService::CreateResource(productEsnKey, seconds, m_mainTaskId)) {
            continue;
        }
        if (productController->BatchQueryRemoteDevice(basicEsn, basicDeviceID) == MP_SUCCESS) {
            HCP_Log(INFO, MODULE) << "Existed remote device." << HCPENDLOG;
            m_remoteDeviceID = basicDeviceID;
            sleep(NUM_3);
            HetroCommonService::DeleteResource(productEsnKey, m_mainTaskId);
            return true;
        }

        if (!CreateRemoteDeviceTask(basicController, productController)) {
            HetroCommonService::DeleteResource(productEsnKey, m_mainTaskId);
            return false;
        }
        HetroCommonService::DeleteResource(productEsnKey, m_mainTaskId);
        return true;
    } while (++checkCount < NUM_3);

    if (WaitRemoteDeviceNormal(productController, basicEsn)) {
        HetroCommonService::DeleteResource(productEsnKey, m_mainTaskId);
        return true;
    }
    HetroCommonService::DeleteResource(productEsnKey, m_mainTaskId);
    return false;
}

bool NasReplicationService::WaitRemoteDeviceNormal(std::shared_ptr<DoradoNas>& productController,
    std::string basicEsn)
{
    // 等待3min,检查创建远端设备是否成功。
    int checkCount = 0;
    std::string basicDeviceID;
    do {
        if (productController->BatchQueryRemoteDevice(basicEsn, basicDeviceID) == MP_SUCCESS) {
            m_remoteDeviceID = basicDeviceID;
            sleep(NUM_3);
            HCP_Log(INFO, MODULE) << "Existed remote device, continue to do next step." << HCPENDLOG;
            return true;
        }
        sleep(NUM_3);
    } while (++checkCount < NUMBER60);
    return false;
}

bool NasReplicationService::CreateRemoteDeviceTask(std::shared_ptr<DoradoNas>& basicController,
    std::shared_ptr<DoradoNas>& productController)
{
    // 远端设备不存在的情况下创建远端设备并绑定端口组
    if (!CreateRemoteDeviceAdministrator(basicController)) {
        HCP_Log(ERR, MODULE) << "CreateRemoteDeviceAdministrator failed" << HCPENDLOG;
        return false;
    }
    if (!CreateRemoteDeviceTaskInner(basicController, productController)) {
        HCP_Log(ERR, MODULE) << "CreateRemoteDeviceTask failed." << HCPENDLOG;
        return false;
    }
    return true;
}

// 故障时修复远端设备，无需修复或修复成功为true
bool NasReplicationService::RecoverRemoteDeviceLink(std::shared_ptr<DoradoNas>& basicController,
    std::shared_ptr<DoradoNas>& productController, int healthStatus)
{
    if (healthStatus != REMOTE_DEVICE_STATUS_FAULT) {
        HCP_Log(WARN, MODULE) << "RemoteDevice health status is normal" << HCPENDLOG;
        return true;
    }
    HCP_Log(WARN, MODULE) << "RemoteDevice health status is fault, it will be relinked" << HCPENDLOG;
    return false;
}

bool NasReplicationService::GetPairInfoInProduct(std::shared_ptr<DoradoNas>& productController,
    std::shared_ptr<DoradoNas>& basicController, std::string& pairID)
{
    std::string basicEsn = "";
    if (basicController->GetESN(basicEsn) != MP_SUCCESS) {
        return false;
    }
    if (productController->GetReplicationPairID(m_productFSID, basicEsn, pairID) != MP_SUCCESS) {
        HCP_Log(ERR, MODULE) << "Not found pair. pairID: "<< pairID
                               <<", remoteDeviceESN:" << basicEsn << HCPENDLOG;
        return false;
    }
    HCP_Log(DEBUG, MODULE) << "GetPairInfoInProduct. pairID: "<< pairID << HCPENDLOG;
    return true;
}

bool NasReplicationService::GetPairInfoByID(std::shared_ptr<DoradoNas>& deviceController, const std::string& pairID,
    ReplicationPairInfo& replicationPairInfo)
{
    if (pairID == "") {
        HCP_Log(WARN, MODULE) << "Query ReplicationPair failed. pairId is NULL" << HCPENDLOG;
        return false;
    }
    replicationPairInfo.pairID = pairID;
    int ret = deviceController->QueryReplication(replicationPairInfo);
    if (ret != MP_SUCCESS) {
        HCP_Log(ERR, MODULE) << "Query ReplicationPair failed. pairId = " << pairID << HCPENDLOG;
        return false;
    }
    HCP_Log(DEBUG, MODULE) << "Get pairInfo by pairID: "<< pairID << " success" << HCPENDLOG;
    return true;
}


bool NasReplicationService::CreateFileSystemInRemoteDevice(std::shared_ptr<DoradoNas>& localController,
    std::shared_ptr<DoradoNas>& remoteController)
{
    unsigned char bytes [RANDOM_MAX_LEN];
    if (GetRandBytesFixLength(bytes, RANDOM_MAX_LEN) <= 0) {
        HCP_Log(ERR, MODULE) << "Generate random number failed." << HCPENDLOG;
        return false;
    }
    std::string randomNum;
    for (auto byte:bytes) {
        randomNum += std::to_string(byte);
    }
    DeviceDetails localInfo;
    if (localController->Query(localInfo) != MP_SUCCESS) {
        HCP_Log(ERR, MODULE) << "FileSystem: " << localInfo.deviceName
            << " doesn't exist in productStorage." << HCPENDLOG;
        return false;
    }
    std::string deviceName = "NAS_Huaage_" + localInfo.deviceName + "_" + randomNum;
    NameLegalization(deviceName);
    remoteController->SetResourceName(deviceName);
    uint64_t capacity = localInfo.totalCapacity / TWO_KB;
    int ret = remoteController->Create(capacity);
    if (ret != MP_SUCCESS) {
        HCP_Log(ERR, MODULE) << "FileSystem in RemoteDevice create failed. localFileSystem: "
            << localInfo.deviceName << HCPENDLOG;
        return false;
    }
    HCP_Log(DEBUG, MODULE) << "Create FileSystem in RemoteDevice success." << HCPENDLOG;
    DeviceDetails remoteInfo;
    ret = remoteController->Query(remoteInfo);
    if (ret != MP_SUCCESS) {
        HCP_Log(ERR, MODULE) << "FileSystem: " << WIPE_SENSITIVE(remoteInfo.deviceName)
        << " doesn't exist." << HCPENDLOG;
        return false;
    }
    return true;
}

void NasReplicationService::DeleteFileSystemInRemoteDevice(std::shared_ptr<DoradoNas>& deviceController)
{
    if (deviceController->Delete() != MP_SUCCESS) {
        HCP_Log(ERR, MODULE) << "FileSystem in RemoteDevice delete failed. localFileSystem: "
            << HCPENDLOG;
        return;
    }
    HCP_Log(DEBUG, MODULE) << "Delete FileSystem in RemoteDevice success." << HCPENDLOG;
    return;
}

bool NasReplicationService::CreateReplicationPair(std::shared_ptr<DoradoNas>& deviceController,
    const int& localResId, const std::string& remoteDevId, int& remoteResId, std::string &pairID)
{
    HCP_Log(INFO, MODULE) << "CreateReplicationPair. localResId: "<< localResId
        << ", remoteResId:" << remoteResId << ", remoteDeviceID:" << remoteDevId
        <<", qos:" << m_qos << HCPENDLOG;
    int ret = deviceController->CreateReplicationWithSnapTag(localResId, remoteDevId,
        m_qos, SNAP_TAG, remoteResId, pairID);
    if (ret != MP_SUCCESS) {
        HCP_Log(ERR, MODULE) << "CreateReplicationPair failed " << HCPENDLOG;
        return false;
    }
    m_pairID = pairID;
    HCP_Log(INFO, MODULE) << "CreateReplicationPair success. pairID: "<< pairID << HCPENDLOG;
    return true;
}

bool NasReplicationService::ActiveReplicationPair(std::shared_ptr<DoradoNas>& localController,
    const std::string& pairID, int64_t& errorCode)
{
    ReplicationPairInfo replicationPairInfo;
    if (pairID == "") {
        HCP_Log(WARN, MODULE) << "PairId is NULL " << HCPENDLOG;
        return false;
    }
    if (!GetPairInfoByID(localController, pairID, replicationPairInfo)) {
        HCP_Log(ERR, MODULE) << "Query ReplicationPair failed. pairId = " << pairID << HCPENDLOG;
        return false;
    }
    if (replicationPairInfo.status != SYNCHRONIZING && replicationPairInfo.status != INTERRUPTED &&
        replicationPairInfo.status != INVALIID) {
        if (localController->ActiveReplication(pairID) != MP_SUCCESS) {
            HCP_Log(ERR, MODULE) << "Active ReplicationPair failed. pairId = " << pairID << HCPENDLOG;
            errorCode = localController->GetErrorCode();
            return false;
        }
        HCP_Log(INFO, MODULE) << "Active ReplicationPair success." << HCPENDLOG;
        return true;
    }
    HCP_Log(ERR, MODULE) << "Active ReplicationPair failed. pairId = " << pairID
                         << ", status = " << replicationPairInfo.status << HCPENDLOG;
    return false;
}

bool NasReplicationService::SplitReplicationPair(std::shared_ptr<DoradoNas>& deviceController,
    const std::string& pairID)
{
    ReplicationPairInfo replicationPairInfo;
    if (pairID == "") {
        HCP_Log(WARN, MODULE) << "PairId is NULL " << HCPENDLOG;
        return true;
    }
    if (!GetPairInfoByID(deviceController, pairID, replicationPairInfo)) {
        HCP_Log(ERR, MODULE) << "Query ReplicationPair failed. pairId = " << pairID << HCPENDLOG;
        return false;
    }
    if (replicationPairInfo.status == SPLIT) {
        HCP_Log(INFO, MODULE) << "ReplicationPair has been splited. pairId = " << pairID << HCPENDLOG;
        return true;
    }
    if (replicationPairInfo.status != SPLIT && replicationPairInfo.status != INVALIID) {
        if (deviceController->SplitReplication(pairID) != MP_SUCCESS) {
            HCP_Log(ERR, MODULE) << "Split ReplicationPair failed. pairId = " << pairID << HCPENDLOG;
            return false;
        }
        HCP_Log(INFO, MODULE) << "Split ReplicationPair success." << HCPENDLOG;
        return true;
    }
    HCP_Log(ERR, MODULE) << "Split ReplicationPair failed. pairId = " << pairID
                         << ", status = " << replicationPairInfo.status << HCPENDLOG;
    return false;
}

bool NasReplicationService::DeleteReplicatonPair(std::shared_ptr<DoradoNas>& deviceController,
    const std::string& pairID)
{
    ReplicationPairInfo replicationPairInfo;
    if (pairID == "") {
        HCP_Log(WARN, MODULE) << "PairId is NULL " << HCPENDLOG;
        return true;
    }
    if (!GetPairInfoByID(deviceController, pairID, replicationPairInfo)) {
        HCP_Log(ERR, MODULE) << "Query ReplicationPair failed. pairId = " << pairID << HCPENDLOG;
        return false;
    }
    if (replicationPairInfo.status != NORMAL && replicationPairInfo.status != TO_BE_RECOVERED &&
        replicationPairInfo.status != SYNCHRONIZING) {
        if (deviceController->DeleteReplication(pairID) != MP_SUCCESS) {
            HCP_Log(ERR, MODULE) << "Delete ReplicationPair failed. pairId = " << pairID << HCPENDLOG;
            return false;
        }
        HCP_Log(INFO, MODULE) << "Delete ReplicationPair success." << HCPENDLOG;
        return true;
    }
    HCP_Log(ERR, MODULE) << "Delete ReplicationPair failed. pairId = " << pairID
                         << ", status = " << replicationPairInfo.status << HCPENDLOG;
    return false;
}

bool NasReplicationService::SecondaryResourceProtectEnable(std::shared_ptr<DoradoNas>& deviceController,
    const std::string& pairID)
{
    ReplicationPairInfo replicationPairInfo;
    if (pairID == "") {
        HCP_Log(WARN, MODULE) << "PairId is NULL " << HCPENDLOG;
        return true;
    }
    if (!GetPairInfoByID(deviceController, pairID, replicationPairInfo)) {
        HCP_Log(ERR, MODULE) << "Query ReplicationPair failed. pairId = " << pairID << HCPENDLOG;
        return false;
    }
    if (replicationPairInfo.status == SPLIT || replicationPairInfo.status == INTERRUPTED) {
        if (deviceController->SecondaryResourceProtectEnable(pairID) != MP_SUCCESS) {
            HCP_Log(ERR, MODULE) << "SecondaryResourceProtectEnable failed. pairId = " << pairID << HCPENDLOG;
            return false;
        }
        HCP_Log(INFO, MODULE) << "SecondaryResourceProtectEnable success." << HCPENDLOG;
        return true;
    }
    HCP_Log(ERR, MODULE) << "SecondaryResourceProtectEnable failed. pairId = " << pairID
                         << ", status = " << replicationPairInfo.status << HCPENDLOG;
    return false;
}

bool NasReplicationService::SecondaryResourceProtectDisable(std::shared_ptr<DoradoNas>& deviceController,
    const std::string& pairID)
{
    ReplicationPairInfo replicationPairInfo;
    if (pairID == "") {
        HCP_Log(WARN, MODULE) << "PairId is NULL " << HCPENDLOG;
        return true;
    }
    if (!GetPairInfoByID(deviceController, pairID, replicationPairInfo)) {
        HCP_Log(ERR, MODULE) << "Query ReplicationPair failed. pairId = " << pairID << HCPENDLOG;
        return false;
    }
    if (replicationPairInfo.status == SPLIT || replicationPairInfo.status == INTERRUPTED) {
        if (deviceController->SecondaryResourceProtectDisable(pairID) != MP_SUCCESS) {
            HCP_Log(ERR, MODULE) << "SecondaryResourceProtectDisable failed. pairId = " << pairID << HCPENDLOG;
            return false;
        }
        HCP_Log(INFO, MODULE) << "SecondaryResourceProtectDisable success." << HCPENDLOG;
        return true;
    }
    HCP_Log(ERR, MODULE) << "SecondaryResourceProtectDisable failed. pairId = " << pairID
                         << ", status = " << replicationPairInfo.status << HCPENDLOG;
    return false;
}

bool NasReplicationService::SwitchOverPrimaryAndSecondResource(std::shared_ptr<DoradoNas>& deviceController,
    const std::string& pairID)
{
    ReplicationPairInfo replicationPairInfo;
    if (!GetPairInfoByID(deviceController, pairID, replicationPairInfo)) {
        HCP_Log(ERR, MODULE) << "Query ReplicationPair failed. pairId = " << pairID << HCPENDLOG;
        return false;
    }
    if (replicationPairInfo.status == SPLIT && replicationPairInfo.secresDataStatus == COMPLETE) {
        if (deviceController->SwitchOverPrimaryAndSecondResource(pairID) != MP_SUCCESS) {
            HCP_Log(ERR, MODULE) << "SwitchOverPrimaryAndSecondResource failed. pairId = " << pairID << HCPENDLOG;
            return false;
        }
        HCP_Log(INFO, MODULE) << "SwitchOverPrimaryAndSecondResource success." << HCPENDLOG;
        return true;
    }
    HCP_Log(ERR, MODULE) << "SwitchOverPrimaryAndSecondResource failed. pairId = " << pairID
                         << ", status = " << replicationPairInfo.status << HCPENDLOG;
    return false;
}

bool NasReplicationService::NameLegalization(std::string &name)
{
    std::vector<std::string>::iterator iter;
    int begin = -1;
    std::string result = "";
    for (unsigned int i = 0; i < name.length(); i++) {
        if ((begin = illegalChar.find(name[i], begin + 1)) == std::string::npos) {
            result = result + name[i];
        }
    }
    name = result;
    return true;
}

std::string NasReplicationService::GeneratePasswordOnce()
{
    std::vector<std::string> paramList;
    std::vector<std::string> output;
    std::vector<std::string> erroutput;
    std::string cmd = "tr -dc 'a-zA-Z0-9*.@_' < /dev/random | head -c 16";
    HCP_Log(DEBUG, MODULE) << "Begin to Generate Password" << HCPENDLOG;

    int ret = Module::runShellCmdWithOutput(INFO, MODULE, 0, cmd, paramList, output, erroutput);
    std::string msg;
    for (auto& it : output) {
        msg += it;
    }
    return msg;
}

std::string NasReplicationService::GeneratePassword()
{
    while (true) {
        std::string msg = GeneratePasswordOnce();
        std::string::const_iterator iter = msg.begin();
        std::string::const_iterator iterEnd = msg.end();
        bool flag = any_of(iter, iterEnd, boost::is_any_of("[\\/:*?""<>|;!@#$%^&(){}[]_-+='',.~"));
        if (flag) {
            HCP_Log(DEBUG, MODULE) << "Generate Password success." << HCPENDLOG;
            return msg;
        }
    }
}

void NasReplicationService::GetReplicationSpeed(std::shared_ptr<DoradoNas>& deviceController,
    const std::string& pairID, uint64_t& speed)
{
    if (deviceController != NULL) {
        if (deviceController->QueryReplicationPerformance(pairID, speed) != MP_SUCCESS) {
            HCP_Log(WARN, MODULE) << "Failed to get speed of replication." << HCPENDLOG;
            speed = 0;
        }
    }
    return;
}

bool NasReplicationService::OpenPerformanceStatisticSwitch(std::shared_ptr<DoradoNas>& deviceController)
{
    if (deviceController->OpenPerformanceStatisticSwitch() != MP_SUCCESS) {
        HCP_Log(ERR, MODULE) << "OpenPerformanceStatisticSwitch failed. " << HCPENDLOG;
        return false;
    }
    return true;
}

bool NasReplicationService::CreateSnapShotInStorage(std::shared_ptr<DoradoNas>& deviceController,
    const std::string& snapshotname, std::string& snapshotID)
{
    if (deviceController->CreateSnapshotWithSnapTag(snapshotname, SNAP_TAG, snapshotID) != MP_SUCCESS) {
        HCP_Log(ERR, MODULE) << "CreateSnapShotInStorage failed. " << HCPENDLOG;
        return false;
    }
    return true;
}

bool NasReplicationService::DeleteSnapShotInStorage(std::shared_ptr<DoradoNas>& deviceController,
    const std::string& snapshotName)
{
    if (snapshotName == "") {
        HCP_Log(WARN, MODULE) << "snapshotName is NULL" << HCPENDLOG;
        return true;
    }
    std::string snapshotId;
    if (deviceController->QuerySnapshot(snapshotName, snapshotId) == MP_SUCCESS) {
        HCP_Log(INFO, MODULE) << "Succeed to querying snapShot " << snapshotName
            << " in storage successfully." << HCPENDLOG;
        if (deviceController->DeleteSnapshot(snapshotName) != MP_SUCCESS) {
            HCP_Log(ERR, MODULE) << "Delete snapShot " << WIPE_SENSITIVE(snapshotName)
            << " in storage failed." << HCPENDLOG;
            return false;
        }
        return true;
    }
    HCP_Log(WARN, MODULE) << "no found SnapShot[" << WIPE_SENSITIVE(snapshotName) << "] in storage." << HCPENDLOG;
    // 快照不存在场景不应报删除快照失败警告，所以返回true
    return true;
}

bool NasReplicationService::RevertSnapShotInStorage(std::shared_ptr<DoradoNas>& deviceController,
    const std::string& snapshotName, const std::string &snapshotId, int64_t &errorCode)
{
    errorCode = deviceController->RollBackBySnapShotId(snapshotName, snapshotId);
    if (errorCode != MP_SUCCESS) {
        HCP_Log(ERR, MODULE) << "Revert snapShot " << WIPE_SENSITIVE(snapshotName)
        <<" in localstorage failed." << HCPENDLOG;
        return false;
    }
    return true;
}

int NasReplicationService::QueryRevertEndInfoInStorage(std::shared_ptr<DoradoNas>& deviceController,
    std::string& rollbackRate, std::string& rollbackStatus, std::string& endTime, std::string& snapshotId)
{
    DeviceDetails info;
    if (deviceController->Query(info) != MP_SUCCESS) {
        HCP_Log(ERR, MODULE) << "Not found local fs" << HCPENDLOG;
        return MP_FAILED;
    }
    endTime = "";
    int errorCode = deviceController->QuerySnapshotRollBackStatusV4(
        std::to_string(info.deviceId), snapshotId, rollbackStatus, endTime);
    if (errorCode != MP_SUCCESS) {
        HCP_Log(ERR, MODULE) << "Query fs " << WIPE_SENSITIVE(info.deviceName)
            <<" revert info in localstorage failed." << HCPENDLOG;
        return MP_FAILED;
    }
    return errorCode;
}

bool NasReplicationService::QueryRevertInfoInStorage(std::shared_ptr<DoradoNas>& deviceController,
    std::string& rollbackRate, std::string& rollbackStatus, int64_t &errorCode)
{
    DeviceDetails info;
    if (deviceController->Query(info) != MP_SUCCESS) {
        HCP_Log(ERR, MODULE) << "Not found local fs" << HCPENDLOG;
        return false;
    }
    errorCode = deviceController->QueryRevertInfo(info.deviceName, rollbackRate, rollbackStatus);
    if (errorCode != MP_SUCCESS) {
        HCP_Log(ERR, MODULE) << "Query fs " << WIPE_SENSITIVE(info.deviceName)
            <<" revert info in localstorage failed." << HCPENDLOG;
        return false;
    }
    return true;
}
} // namespace NasRepServiceObjOp